using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.BackgroundJobs;
using Abp.Dependency;
using Abp.Domain.Entities;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Localization;
using Abp.Runtime.Caching;
using Abp.Runtime.Session;
using Abp.UI;
using Mt.Site.Application.ContentManagement.FrontEnd.Dto;
using Mt.Site.Application.FileUpload.Dto;
using Mt.Site.Core;
using Mt.Site.Core.BackGroundJobs;
using Mt.Site.Core.BackGroundJobs.Storage;
using Mt.Site.Core.Base.Utils.FileIO.Upload;
using Mt.Site.Core.ContentManagement.Entities;
using Mt.Site.Core.CustomConfigurations;
using Mt.Site.Core.Navigations.Entities;
using Mt.Site.Core.StaticPages;
using Mt.Site.Core.Storage;
using Mt.Site.Core.Synchronization;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Mt.Site.Core.Base.Utils.FileIO.Upload.UpfileTemplate;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Core.Storage.File_;
using Mt.Site.Core.Base.Settings;

namespace Mt.Site.Application.FileUpload.Impl
{
    public class FileUploadHelper : MtSiteDomainServiceBase, IFileUploadHelper, ITransientDependency
    {
        private readonly IStaticPageHelper _staticPageHelper;
        private readonly IBackgroundJobManager _backgroundJobManager;

        private readonly ISynchronizationHelper _synchronizationHelper;

        private readonly IFindEntityTypeHelper _findEntityTypeHelper;

        private readonly IRepository<Menu, Guid> _menuRepository;

        private readonly IRepository<Content, Guid> _contentRepository;

        private readonly ICacheManager _cacheManager;

        private readonly ICustomConfigManager _customConfigManager;

        private readonly IPermissionChecker _permissionChecker;
        private readonly IPermissionManager _permissionManager;

        private readonly IMtConfiguration _mtConfiguration;

        //private readonly IRepository<ContentStorage, Guid> _contentStorageRepository;

        public FileUploadHelper(
            IRepository<Menu, Guid> menuRepository,
            IBackgroundJobManager backgroundJobManager,
            ISynchronizationHelper synchronizationHelper,
            IFindEntityTypeHelper findEntityTypeHelper,
            IRepository<Content, Guid> contentRepository,
            ICustomConfigManager customConfigManager,
            IPermissionChecker permissionChecker,
            IPermissionManager permissionManager,
            IStaticPageHelper staticPageHelper,
            ICacheManager cacheManager,
            IMtConfiguration mtConfiguration)
        {

            _contentRepository = contentRepository;

            _menuRepository = menuRepository;
            _backgroundJobManager = backgroundJobManager;
            _synchronizationHelper = synchronizationHelper;
            _findEntityTypeHelper = findEntityTypeHelper;
            _customConfigManager = customConfigManager;
            _permissionChecker = permissionChecker;
            _permissionManager = permissionManager;
            _staticPageHelper = staticPageHelper;

            _cacheManager = cacheManager;
            _mtConfiguration = mtConfiguration;
        }

        #region 获取配置信息

        public async Task<UpfileTemplate> GetUploadConfigurationAsync(string customKey)
        {
            var upfileTemplate = await _customConfigManager.GetUploadConfigurationAsync(customKey);

            for (int i = 0; i < upfileTemplate.Count; i++)
            {
                var templatePara = upfileTemplate[i];
                Alter(templatePara.UpFileInfo);
                Alter(templatePara.ThumbnailUpFileInfo);
            }

            return upfileTemplate;
        }


        public async Task<Para> GetUpFileConfig(string customKey, string alias)
        {
            Para para = await _customConfigManager.GetUploadFileInfoAsync(customKey, alias);

            if (para == null)
            {
                throw new UserFriendlyException(L("ThereIsNoSuchConfigurationDataWithGivenKey"));
            }

            await CheckPermission(para.PermissionName);

            Alter(para.UpFileInfo);
            Alter(para.ThumbnailUpFileInfo);

            return para;
        }

        private void Alter(UpFileInfo info)
        {
            if (info == null)
            {
                return;
            }

            //内存限制(直接上传的文件限制其文件大小必须小于30M)
            if (info.MaxSize > _mtConfiguration.FileUploadMaxSize)
            {
                info.MaxSize = _mtConfiguration.FileUploadMaxSize;
            }

            //文件格式的白名单过滤
            var exNameList = new List<string>();
            foreach (var item in info.ExNames)
            {
                var lower = item.ToLower();
                if (_mtConfiguration.FileDownAllow_TenantSide.Contains($".{lower}"))
                {
                    exNameList.Add(lower);
                }
            }
            info.ExNames = exNameList.ToArray();
        }

        #endregion

        #region EF

        /// <summary>
        /// 创建文件信息
        /// </summary>
        /// <returns></returns>
        public async Task<FileInfoDto> CreateFileInfo(
            IFileObjectManager fileObjectManager, Guid contentId, string alias, bool isTemporary, 
            UploadFileInfo uploadFileInfo, string fileOriginName, string exName, FileScoureName sourceName)
        {
            var model = new FileObject
            {
                FileUrl = uploadFileInfo.FileUrl,
                FileName = fileOriginName,
                Alias = alias,
                ContentId = contentId,
                exName = exName,
                Scoure = "upload",
                Size = uploadFileInfo.Dimensions,
                IsActive = true,
                AutoSynchronous = true,
                FileLength = uploadFileInfo.FileSize,
                IsFromLibrary = (sourceName == FileScoureName.FromLibrary),
                Sha1 = uploadFileInfo.Sha1,
                IsTemporary = isTemporary
            };

            model.Order = await GetMaxOrder(fileObjectManager, model.ContentId, model.Alias);

            var fileObject = await fileObjectManager.AddAsync(model);                      // 写入文件信息

            var result = ObjectMapper.Map<FileInfoDto>(fileObject);
            result.Status = true;

            await fileObjectManager.RemoveTemporaryFileAsync();

            return result;
        }


        public async Task<FileInfoDto> CreateFileInfo2(IFileObjectManager fileObjectManager, SelectFromLibraryInput input, string fileName, string exName)
        {
            var model = new FileObject
            {
                FileUrl = input.FileUrl,
                FileName = fileName,
                Alias = input.Alias,
                ContentId = input.ContentId,
                exName = exName,
                Scoure = "select",
                Size = input.Dimensions,
                IsActive = true,
                AutoSynchronous = true,
                FileLength = input.FileLength,
                IsFromLibrary = true,
                Sha1 = input.Sha1,
                IsTemporary = input.IsTemporary
            };

            model.Order = await GetMaxOrder(fileObjectManager, model.ContentId, model.Alias);

            var fileObject = await fileObjectManager.AddAsync(model);                      // 写入文件信息

            var result = ObjectMapper.Map<FileInfoDto>(fileObject);
            result.Status = true;

            await fileObjectManager.RemoveTemporaryFileAsync();

            return result;
        }

        private async Task<int> GetMaxOrder(IFileObjectManager fileObjectManager, Guid contentId, string alias)
        {
            var query = from file in fileObjectManager.FileObjectRepository.GetAll()
                        where file.ContentId == contentId && file.Alias == alias
                        orderby file.Order descending
                        select file;

            var m = await query.FirstOrDefaultAsync();
            if (m != null)
            {
                return m.Order + 1;
            }
            else
            {
                return 1;
            }
        }


        #endregion

        #region 预留的钩子方法

        public async Task OnDeleted(string fileUrl, Guid id)
        {
            await Task.FromResult(0);
        }

        public async Task OnEdited(string fileUrl, Guid id, bool isPreViewImgUrl)
        {
            await Task.FromResult(0);
        }

        public async Task OnMultiUploaded(string fileUrl, Guid id)
        {
            await Task.FromResult(0);
        }

        public async Task OnSingleUploaded(string fileUrl, Guid id)
        {
            await Task.FromResult(0);
        }

        #endregion

        #region 静态页及数据同步

        public async Task RemoveStaticPageAndSyncFileObject(Guid contentId, Guid fileObjectId, OptionType optionType, IAbpSession AbpSession)
        {
            if (MtSiteConsts2.BackgroundJobs)
            {
                var entity = await FindContentAndRemoveStaicPages(contentId, AbpSession.GetTenantId(), fileObjectId);

                if (entity != null && FilterFileObjectForSync(entity) != null)
                {
                    await _backgroundJobManager.EnqueueAsync<SynchorizationFileObjectJob, BaseAndMenuIdArgs>(
                            new BaseAndMenuIdArgs
                            {
                                FromLanguage = AbpSession.CultureName(),
                                Id = fileObjectId,
                                OperateType = optionType,
                                FromTenantId = AbpSession.GetTenantId(),
                            });
                }
            }
        }


        private async Task<Entity<Guid>> FindContentAndRemoveStaicPages(Guid contentId, int tenantId, Guid fileObjectId)
        {
            var entity = _findEntityTypeHelper.FindContentTypeByGuid(
                contentId, tenantId,
                new List<Type> {
                    typeof(Content),
                    typeof(Content2)
                });
            await DeleteAssociateStaticPages(entity, fileObjectId);
            return entity;
        }

        /// <summary>
        /// 删除关联附件的相关模块
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="fileObjectId"></param>
        private async Task DeleteAssociateStaticPages(Entity<Guid> entity, Guid fileObjectId)
        {
            if (entity is Content)
            {
                var solution = entity as Content;
                _staticPageHelper.DeleteListAndDetailPage(solution.TenantId, solution.Language, solution.MenuId, solution.AuxiliaryId);

                await _cacheManager.GetCache<string, PagedResultDto<_ContentListDto>>(ApiCacheNames.ContentPagedList).ClearAsync();
            }
            else if (entity is Content2)
            {
                var solution = entity as Content2;
                _staticPageHelper.DeleteListAndDetailPage(solution.TenantId, solution.Language, solution.MenuId, solution.AuxiliaryId);

                await _cacheManager.GetCache<string, PagedResultDto<_ContentListDto>>(ApiCacheNames.Content2PagedList).ClearAsync();
            }
        }
        private Entity<Guid> FilterFileObjectForSync(Entity<Guid> entity)
        {
            //if (entity is MarketActivityContent || entity is Ezine || entity is RelevantInfo)
            //{
            //    return null;
            //}
            return entity;
        }


        #endregion

        private async Task<bool> IsGrantedAsync(string permissionName)
        {
            return await _permissionChecker.IsGrantedAsync(permissionName);
        }

        private async Task CheckPermission(string permissionName)
        {
            if (!permissionName.IsNullOrWhiteSpace() && !(await IsGrantedAsync(permissionName)))
            {
                var permission = _permissionManager.GetPermissionOrNull(permissionName);
                if (permission != null)
                {
                    string localizedPermissionName;
                    if (permission.DisplayName != null && permission.DisplayName is LocalizableString)
                    {
                        var p = permission.DisplayName as LocalizableString;
                        localizedPermissionName = LocalizationManager.GetString(p.SourceName, p.Name);
                    }
                    else
                    {
                        localizedPermissionName = permissionName;
                    }

                    throw new AbpAuthorizationException(
                        $"Required permissions are not granted. At least one of these permissions must be granted: {localizedPermissionName}");
                }
                else
                {
                    throw new AbpAuthorizationException("Required permission does not exist");
                }
            }
        }

    }
}
